package at.fhooe.im440.pandora.entities;

import java.awt.Color;
import java.awt.Graphics2D;
import java.awt.event.KeyEvent;
import java.awt.geom.Point2D;
import java.util.Vector;

import at.fhooe.im440.pandora.collision.CircleCollider;
import at.fhooe.im440.pandora.core.Core;
import at.fhooe.im440.pandora.events.EventListener;
import at.fhooe.im440.pandora.io.GameAction;

public class Pacman extends Entity implements EventListener {

	private static final float STEPWIDTH = 16.0f;
	private static final double PI = Math.PI;
	private static final double TWIST = PI / 25;
	private static final float PACMAN_STARTANGLE = 180.0f;
	private static final String TYPE = "Pacman";
	private static final String COLLISION = "Collision";
	private static final float GNAW_SPEED = 15.0f;
	private static final float GNAW_ANGLE = 30.0f;
	
	private Color m_Color;

	private float height;
	private float width;

	private float x;
	private float y;

	private float angle;

	private float bulletCooldown = -1.0f;
	private float cooldownTime = 1.0f;

	private GameAction left;
	private GameAction right;
	private GameAction up;
	private GameAction down;
	private GameAction shoot;
	
	private float openingTime = 0.0f;
	
	//Physic movement
	private static final double ROTATE_MAX_SPEED = 30;
	private static final double ROTATE_ACCELERATION = 30;
	private static final double ROTATE_FRICTION = 40;
	private static final double TRANSLATE_MAX_SPEED = 400;
	private static final double TRANSLATE_ACCELERATION = 400;
	private static final double TRANSLATE_FRICTION = 60;
	private double[] position;
	private double[] velocity;
	private double[] acceleration;
	private double dX;
	private double dY;
	private double angularPosition;
	private int level_width;
	private int level_height;

	public Pacman(Core core, String name, float x, float y, float width,
			float height) {
		super(name, core, x+width/2.0f, y+width/2.0f, width);
		this.x = x;
		this.y = y;
		this.width = width;
		this.height = height;
		level_width = core.graphicsConfig.Width;
		level_height = core.graphicsConfig.Height;
		angle = PACMAN_STARTANGLE;
		m_Color = Color.yellow;
		core.collisionTester.addPlayerObject(super.getCollisionObject());
		
		//Physics movement
		position = new double[3];
		velocity = new double[3];
		acceleration = new double[3];
		
		setUp();
	}

	@Override
	public void render(Graphics2D g) {
		
		g.setColor(m_Color);
		int openingAngle = (int) (Math.sin(openingTime * GNAW_SPEED) * GNAW_ANGLE);
		
		int angleSize = 300 - openingAngle * 2;
		g.rotate(angularPosition, x + width / 2, y + height / 2);
		g.fillArc((int) x, (int) y, (int) width, (int) height, openingAngle + 300, angleSize);
		if(core.isDebug)
		{
			super.drawCollider(g);
		}
		g.rotate(-angularPosition, x + width / 2, y + height / 2);
		
//		g.setColor(Color.yellow);
//		int openingAngle = (int) ((Math.sin(x / 40.0) * 30) + (Math.sin(y / 40.0) * 30));
//		int angleSize = 300 - openingAngle;
//		g.rotate(angle, x + width / 2, y + height / 2);
//		g.fillArc((int) x, (int) y, (int) width, (int) height, openingAngle + 30, angleSize);
//		g.rotate(-angle, x + width / 2, y + height / 2);
	}

	@Override
	public void update() {
		
		// Angle
		openingTime += core.getDeltaTime();
		if (openingTime > Math.PI) {
			openingTime = 0.0f;
		}
			
		if (left.isPressed()) {
			//angle = (float) ((angle + TWIST) % (2 * PI));
			dX = -1.0;
		}
		else if(right.isPressed()){
			//angle = (float) ((angle - TWIST) % (2 * PI));
			dX = 1.0;
		}
		else
		{
			dX = 0;
		}

		if (up.isPressed()) {
			//y += STEPWIDTH * Math.sin(angle);
			//x += STEPWIDTH * Math.cos(angle);
			//super.updateCollider(x+width/2.0f, y+width/2.0f);
			dY = 1.0;
		}
		else if (down.isPressed()) {
			//y -= STEPWIDTH * Math.sin(angle);
			//x -= STEPWIDTH * Math.cos(angle);
			//super.updateCollider(x+width/2.0f, y+width/2.0f);
			dY = -1.0;
		}
		else
		{
			dY = 0;
		}
		
		if (shoot.isPressed()) {
			//if (bulletCooldown <= 0.0f) {
				String uniqueBulletName = "Bullet" + " " + System.currentTimeMillis();
				core.entityManager.addEntity(new Bullet(uniqueBulletName, core,
						(float) getPosition().getX() + width / 2, (float) getPosition().getY() + height / 2, (float) angularPosition-300 , 50));
				bulletCooldown = cooldownTime;
			//}
		}

		if (bulletCooldown >= 0.0f) {
			bulletCooldown -= core.getDeltaTime();
		}
		
		movePacman(dX, dY);
		updatePosition(core.getDeltaTime());
		x = (float) position[0];
		y = (float) position[1];
		angularPosition = position[2];
		super.updateCollider(x+width/2.0f, y+width/2.0f);

	}
	
	private void movePacman(double vx, double vy) {
		// ROTATION
		// Rotation acceleration
		acceleration[2] =  ((-vx) * ROTATE_ACCELERATION);
		if (vx != 0) 
		{
			// Rotation speed clamping
			if (velocity[2] < -ROTATE_MAX_SPEED || velocity[2] > ROTATE_MAX_SPEED)
				acceleration[2] = 0;
		} else {
			// Rotation friction
			if (velocity[2] < 0)
			acceleration[2] += ROTATE_FRICTION;
			if (velocity[2] > 0)
			acceleration[2] -= ROTATE_FRICTION;
		}
		// Rotation value clamping between 0 and 2PI
		position[2] =  (position[2] % (2 * Math.PI));
		if (position[2] < 0)
			position[2] =  (2.0 * Math.PI + position[2]);
		// TRANSLATION
		if (vy != 0) 
		{
			// Speed control
			if (velocity[0] > TRANSLATE_MAX_SPEED * Math.abs(Math.sin(position[2]))) 
			{
				acceleration[0] = 0;
				velocity[0] =  (TRANSLATE_MAX_SPEED * Math.abs(Math.sin(position[2])));
			} 
			else if (velocity[0] < -TRANSLATE_MAX_SPEED * Math.abs(Math.sin(position[2]))) 
			{
				acceleration[0] = 0;
				velocity[0] =  (-TRANSLATE_MAX_SPEED * Math.abs(Math.sin(position[2])));
			} else
				acceleration[0] =  (-Math.sin(position[2]) * vy * TRANSLATE_ACCELERATION);
			if (velocity[1] > TRANSLATE_MAX_SPEED * Math.abs(Math.cos(position[2]))) 
			{
				acceleration[1] = 0;
				velocity[1] =  (TRANSLATE_MAX_SPEED * Math.abs(Math.cos(position[2])));
			} 
			else if (velocity[1] < -TRANSLATE_MAX_SPEED * Math.abs(Math.cos(position[2]))) 
			{
				acceleration[1] = 0;
				velocity[1] =  (-TRANSLATE_MAX_SPEED * Math.abs(Math.cos(position[2])));
			} 
			else
				acceleration[1] =  (Math.cos(position[2]) * vy * TRANSLATE_ACCELERATION);
			// Ease Out
		} 
		else 
		{
			if ((velocity[0] > -0.04 && velocity[0] < 0) || (velocity[0] < 0.04 && velocity[0] > 0))
				acceleration[0] = velocity[0] = 0;
			else if (velocity[0] < 0)
				acceleration[0] =  (Math.abs(Math.sin(position[2])) * TRANSLATE_FRICTION);
			else if (velocity[0] > 0)
				acceleration[0] =  (-Math.abs(Math.sin(position[2])) * TRANSLATE_FRICTION);
			
			if ((velocity[1] > -0.04 && velocity[1] < 0) || (velocity[1] < 0.04 && velocity[1] > 0))
				acceleration[1] = velocity[1] = 0;
			else if (velocity[1] < 0)
				acceleration[1] =  (Math.abs(Math.cos(position[2])) * TRANSLATE_FRICTION);
			else if (velocity[1] > 0)
				acceleration[1] =  (-Math.abs(Math.cos(position[2])) * TRANSLATE_FRICTION);
		}
		// Edge Loop
		if (position[0] > level_width)
			position[0] = -level_width;
		if (position[0] < -level_width)
			position[0] = level_width;
		if (position[1] > level_height)
			position[1] = -level_height;
		if (position[1] < -level_height)
			position[1] = level_height;
	}
	
	private void updatePosition(double dt) {
		for (int i = 0; i < this.position.length; ++i) {
			this.velocity[i] += this.acceleration[i] * dt;
			this.position[i] += this.velocity[i] * dt;
		}
	}

	@Override
	public Point2D getPosition() {
		return new Point2D.Float(x, y);
	}

	@Override
	public void setPosition(float x, float y) {
		this.x = x;
		this.y = y;
	}

	@Override
	public String getType() {
		return TYPE;
	}

	@Override
	public void setUp() {
		core.eventManager.addListener(this, COLLISION);

		left = new GameAction("left");
		right = new GameAction("right");
		up = new GameAction("up");
		down = new GameAction("down");
		shoot = new GameAction("shoot", GameAction.DETECT_INITAL_PRESS_ONLY);

		core.inputManager.mapToKey(left, KeyEvent.VK_LEFT);
		core.inputManager.mapToKey(right, KeyEvent.VK_RIGHT);
		core.inputManager.mapToKey(up, KeyEvent.VK_UP);
		core.inputManager.mapToKey(down, KeyEvent.VK_DOWN);
		core.inputManager.mapToKey(shoot, KeyEvent.VK_SPACE);
	}

	@Override
	public void tearDown() {
		// TODO Auto-generated method stub

	}

	@Override
	public void collided(CircleCollider col) {
		m_Color = new Color((float) Math.random(), (float) Math.random(), (float) Math.random());
	}

}
